package com.wangjiang.foundation.common.exception;

import org.apache.commons.lang3.StringUtils;

import java.io.PrintStream;
import java.io.PrintWriter;

import static com.wangjiang.foundation.common.util.StringUtil.format;

public class CommonException extends RuntimeException {

    private static final long serialVersionUID = 1L;

    private int status = 500;

    private String code;

    public CommonException(String message) {
        this(500, message);
    }

    public CommonException(String message, Object... args) {
        this(500, message, args);
    }

    public CommonException(int status, String message) {
        this(status, null, message, null);
    }

    public CommonException(int status, String message, Object... args) {
        this(status, null, message, args);
    }

    public CommonException(int status, String code, String message) {
        this(status, code, message, null);
    }

    public CommonException(int status, String code, String message, Object... args) {
        super(format(message, args));
        this.status = status;
        this.code = code;
    }

    public CommonException(Throwable cause) {
        super(cause);
    }

    public CommonException(Throwable cause, int status) {
        this(cause, status, null);
    }

    public CommonException(Throwable cause, String message) {
        this(cause, message, null);
    }

    @Deprecated
    public CommonException(String message, Throwable cause) {
        this(cause, message, null);
    }

    public CommonException(Throwable cause, String message, Object... args) {
        this(cause, 500, message, args);
    }

    public CommonException(Throwable cause, int status, String message) {
        this(cause, status, null, message, null);
    }

    public CommonException(Throwable cause, int status, String message, Object... args) {
        this(cause, status, null, message, args);
    }

    public CommonException(Throwable cause, int status, String message, String code) {
        this(cause, status, code, message, null);
    }

    public CommonException(Throwable cause, int status, String code, String message, Object... args) {
        super(format(message, args), cause);
        this.status = status;
        this.code = code;
    }

    public int getStatus() {
        return status;
    }

    public String getCode() {
        return code;
    }

    @Override
    public String getMessage() {
        StringBuilder msg = StringUtils.isBlank(super.getMessage()) ? new StringBuilder() : new StringBuilder(super.getMessage());

        Throwable child;
        for (Object parent = this; (child = this.getNestedException((Throwable) parent)) != null; parent = child) {
            String msg2 = child.getMessage();
            if (msg2 != null) {
                if (msg != null) {
                    msg.append(": ").append(msg2);
                } else {
                    msg = new StringBuilder(msg2);
                }
            }

            if (child instanceof CommonException) {
                break;
            }
        }

        return msg.toString();
    }

    private Throwable getNestedException(Throwable parent) {
        return parent.getCause();
    }

    @Override
    public void printStackTrace() {
        super.printStackTrace();
        Object parent = this;

        Throwable child;
        while ((child = this.getNestedException((Throwable) parent)) != null) {
            System.err.print("Caused by: ");
            child.printStackTrace();
            if (child instanceof CommonException) {
                break;
            }

            parent = child;
        }

    }

    @Override
    public void printStackTrace(PrintStream s) {
        super.printStackTrace(s);
        Object parent = this;
        printStream(new PrintWriter(s), parent);

    }

    private void printStream(PrintWriter s, Object parent) {
        Throwable child;
        while ((child = this.getNestedException((Throwable) parent)) != null) {
            s.print("Caused by: ");
            child.printStackTrace(s);
            if (child instanceof CommonException) {
                break;
            }

            parent = child;
        }
    }

    @Override
    public void printStackTrace(PrintWriter w) {
        super.printStackTrace(w);
        Object parent = this;

        printStream(w, parent);

    }
}